/*
 * INSANE - Interactive Structural Analysis Environment
 *
 * Copyright (C) 2003-2006
 * Universidade Federal de Minas Gerais
 * Escola de Engenharia
 * Departamento de Engenharia de Estruturas
 *
 * Author's email :     insane@dees.ufmg.br
 * Author's Website :   http://www.dees.ufmg.br/insane
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
 
package br.ufmg.dees.insane.draw;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.util.ListIterator;
import java.util.Vector;

/**
 * @author Fonseca, Flavio & Pitangueira, Roque
 * @since	May 2004
 */
public abstract class LineArrowDraw extends Draw {
    
    protected double aValue;
    protected double bValue;
    protected double a;
    protected double b;
    protected LineDraw base;
	protected Point endLoc;
    
//*****************************************************************************
	
    /**
     * Constructor method
     */
    public LineArrowDraw() {
    }
	
//*****************************************************************************
    
    public LineArrowDraw(LineDraw base) {
        this(1, 1, base);
    }
    
//*****************************************************************************
	
    public LineArrowDraw(double aValue, double bValue, LineDraw base) {
        this(0, 100, aValue, bValue, base);
    }
    
//*****************************************************************************
	
    public LineArrowDraw(double a, double b, double aValue,
    double bValue, LineDraw base) {
        this(a, b, aValue, bValue, base, null);
    }
	
//*****************************************************************************
	
    public LineArrowDraw(double a, double b, double aValue,
    double bValue, LineDraw base, Object represents) {
        this.setAValue(aValue);
        this.setBValue(bValue);
        this.base = base;
        this.setA(a);
        this.setB(b);
        this.setRepresents(represents);
    }
	
//*****************************************************************************
	
    public boolean equals(Object obj) {
        if (obj instanceof LineArrowDraw) {
            LineArrowDraw d = (LineArrowDraw)obj;
            if (aValue == d.aValue && bValue == d.bValue && b == d.b && a == d.a && 
			this.getRepresents().equals(d.getRepresents())) {
                return true;
            }
        }
        return false;
    }
	
//*****************************************************************************
	
	public LineDraw getBase() {
		return base;
	}
	
//*****************************************************************************
	
	public void setBase(LineDraw ld) {
		base =ld;
	}
    
//*****************************************************************************
	
    /**
     * Sets the load applied point A force.
     *
     * @param   fa  The desired force to be set.
     */
    public void setAValue(double fa) {
        aValue = fa;
    }
    
//*****************************************************************************
	
    /**
     * Returns the load applied force at point A.
     *
     * @return   The load applied force at point A
     */
    public double getAValue() {
        return aValue;
    }
    
//*****************************************************************************
	
    /**
     * Sets the load applied force at point B.
     *
     * @param   fb  The desired load applied force to be set.
     */
    public void setBValue(double fb) {
        bValue = fb;
    }
    
//*****************************************************************************
	
    /**
     * Returns the load applied force at point B.
     *
     * @return   The load applied force at point B.
     */
    public double getBValue() {
        return bValue;
    }
	
//*****************************************************************************
	
    /**
     * Sets the load A point distance.
     *
     * @param   a1  The double distance to be set.
     */
    public void setA(double a1) {
        if (a1 >= 0 /*&& a1 <= 100*/) {
            a = a1;
        }
    }
    
//*****************************************************************************
	
    /**
     * Returns the load B point distance.
     *
     * @return   The point A distance.
     */
    public double getA() {
        return a;
    }
    
//*****************************************************************************
	
    /**
     * Sets the load B point distance.
     *
     * @param   b1  The double distance to be set.
     */
    public void setB(double b1) {
        if ( b1 >= a /*&& b1 <= 100*/) {
            b = b1;
        }
    }
    
//*****************************************************************************
	
    public void setEndLocation(Point p) {
        endLoc = p;
    }
	
//*****************************************************************************
	
	public Point getEndLocation() {
        return endLoc;
    }
    
//*****************************************************************************
	
    /**
     * Returns the B point distance.
     *
     * @return   The point B distance.
     */
    public double getB() {
        return b;
    }
	
//*****************************************************************************
	
	public void setAngle(double theta) {
		super.setAngle(theta - base.getAngle());
	}
    
//*****************************************************************************
	
    public Dimension getSize() {
		boolean sig;
		
		if ((aValue >= 0) && (bValue >= 0)) {
			sig = true;
		} else if((aValue < 0) && (bValue < 0)) {
			sig = true;
		} else {
			sig = false;
		}
		
		int width = (int)this.getLocation().distance(this.getEndLocation());
		int height;
		if (sig) {
			height =  (int)(Math.max(Math.abs(aValue), Math.abs(bValue))*18*this.getScale());
		} else {
			height =  (int)((Math.abs(aValue) + Math.abs(bValue))*18*this.getScale());
		}	
 		return new Dimension(width, height);
	}
	
//*****************************************************************************
	
    public Rectangle getBounds() {
		boolean sig;
		
		if ((aValue >= 0) && (bValue >= 0)) {
			sig = true;
		} else if((aValue < 0) && (bValue < 0)) {
			sig = true;
		} else {
			sig =false;
		}
		
		double scale = this.getScale();
		double ang = this.getAngle();
       	int x;
        int y;
		if (sig) {
			x =  (int)(this.getLocation().x + Math.max(Math.abs(aValue), Math.abs(bValue))*18*scale*Math.cos(ang));
			y =  (int)(this.getLocation().y - Math.max(Math.abs(aValue), Math.abs(bValue))*18*scale*Math.sin(ang));
		} else {
			x =  (int)(this.getLocation().x + Math.abs(aValue)*18*scale*Math.cos(ang));
			y =  (int)(this.getLocation().y - Math.abs(aValue)*18*scale*Math.sin(ang));
		}	
        int width = this.getSize().width;
        int height = this.getSize().height;
        
        return new Rectangle(x, y, width, height);
    }
    
//*****************************************************************************
	
    /**
     * Returns the draw bounds in device units.
     *
     * @return the draw bounds
     */
    public Shape getRotatedBounds() {
		boolean sig;
		
		if ((aValue >= 0) && (bValue >= 0)) {
			sig = true;
		} else if((aValue < 0) && (bValue < 0)) {
			sig = true;
		} else {
			sig =false;
		}
		GeneralPath path;
		
		double scale = this.getScale();
		double ang = this.getAngle();
       	int x;
        int y;
		if (sig) {
			x =  (int)(this.getLocation().x + Math.abs(aValue)*18*scale*Math.cos(ang));
			y =  (int)(this.getLocation().y - Math.abs(aValue)*18*scale*Math.sin(ang));
			path = new GeneralPath(new Line2D.Double(this.getLocation(), new Point(x,y)));
			x =  (int)(this.getEndLocation().x + Math.abs(bValue)*18*scale*Math.cos(ang));
			y =  (int)(this.getEndLocation().y - Math.abs(bValue)*18*scale*Math.sin(ang));
			path.lineTo(x, y);
			path.lineTo(this.getEndLocation().x, this.getEndLocation().y);
			path.closePath();
		} else {
			x =  (int)(this.getLocation().x + Math.abs(aValue)*18*scale*Math.cos(ang));
			y =  (int)(this.getLocation().y - Math.abs(aValue)*18*scale*Math.sin(ang));
			path = new GeneralPath(new Line2D.Double(this.getLocation(), new Point(x,y)));
			path.lineTo(this.getEndLocation().x, this.getEndLocation().y);
			x =  (int)(this.getEndLocation().x + (-1)*Math.abs(bValue)*18*scale*Math.cos(ang));
			y =  (int)(this.getEndLocation().y - (-1)*Math.abs(bValue)*18*scale*Math.sin(ang));
			path.lineTo(x, y);
			path.closePath();
		}
        
        return path;
   }
   
//*****************************************************************************
   	
    /**
     * Draws in a given Graphics.
     *
     * @see   java.awt.Graphics
     */
    public void draw(Graphics2D g2) {
        
        Vector shapes = getShapes();
		Draw draw;
		
        ListIterator shps = shapes.listIterator();
        while(shps.hasNext()){
            draw = (Draw)shps.next();
			draw.draw(g2);
        }
        
        Color currentColor = g2.getColor();
        
        if (isSelected()) {
            g2.setColor( Color.red );
            g2.draw(this.getRotatedBounds());
        }
        g2.setColor(currentColor);
    }
	
//*****************************************************************************	
//*****************************************************************************
	
	public static class X extends LineArrowDraw {
		
	//*****************************************************************************
		
		/**
		 * Constructor method
		 */
		public X() {
			super();
		}
		
	//*****************************************************************************
		
		public X(LineDraw base) {
			super(base);
		}
		
	//*****************************************************************************
		
		public X(double aValue, double bValue, LineDraw base) {
			super(aValue, bValue, base);
		}
		
	//*****************************************************************************
		
		public X(double a, double b, double aValue, double bValue, LineDraw base) {
			super(a, b, aValue, bValue, base);
		}
		
	//*****************************************************************************
		
		public X(double a, double b, double aValue, double bValue, LineDraw base, Object represents) {
			super(a, b, aValue, bValue, base, represents);
		}
		
	//*****************************************************************************
		
		public Vector<Object> getShapes() {
			Vector<Object> shapes = new Vector<Object>();
			
			ArrowDraw ad;
			double width = this.getLocation().distance(this.getEndLocation());
			
			double val = aValue;
			Point loc;
			double ang = base.getAngle();
			
			int div2 = 30;
			int div = div2;
			double rest = width%div;
			for (int i = 0; i < 29; i++) {
				div2++;
				if ((width%div2) < rest) {
					rest = width%div2;
					div = div2;
				}
			}
			
			int n = (int)(width/div);
			
			int sigA = 1;
			if (val >= 0) {
				sigA = 1;
			} else {
				sigA = -1;
			}
			int sigB;
			
			for (int i = 0; i <= n+1; i++) {
				
				val = ((bValue - aValue) * i * div)/width + aValue;
				if (val >= 0) {
					sigB = 1;
				} else {
					sigB = -1;
				}
				if ((aValue == 0) || (bValue == 0)) {
					sigB = sigA;
				}
				
				loc = new Point(this.getLocation().x + (int)((i * div)*Math.cos(ang)),
					this.getLocation().y + (int)((i * div)*Math.sin(ang)));
				
				ad = new ArrowDraw.Out();
				
				if (i < n) { 
					ad.setLocation(loc);
					/*if ( (i == 0) && (aValue != 0.0)) {
						ad.setLabel(aValue + "");
					}*/
					if (sigA == sigB) {
						ad.setAngle(this.getAngle());
						ad.setValue(val);
					} else {
						ad.setAngle(this.getAngle() - Math.PI);
						ad.setValue((-1)*val);
					}
				} else {
					ad.setLocation(this.getEndLocation());
					/*if (bValue != 0.0) {
						ad.setLabel(bValue + "");
					}*/
					if (sigA == sigB) {
						ad.setAngle(this.getAngle());
						ad.setValue(bValue);
					} else {
						ad.setAngle(this.getAngle() - Math.PI);
						ad.setValue((-1)*bValue);
					}
				}
				ad.setScale(this.getScale());
				
				shapes.add(ad);
			} 
			
			return shapes;
			
		}
		
	//*****************************************************************************
	}
	
//*****************************************************************************	
//*****************************************************************************
	
	public static class Y extends LineArrowDraw {
		
	//*****************************************************************************
		
		/**
		 * Constructor method
		 */
		public Y() {
			super();
		}
		
	//*****************************************************************************
		
		public Y(LineDraw base) {
			super(base);
		}
		
	//*****************************************************************************
		
		public Y(double aValue, double bValue, LineDraw base) {
			super(aValue, bValue, base);
		}
		
	//*****************************************************************************
		
		public Y(double a, double b, double aValue, double bValue, LineDraw base) {
			super(a, b, aValue, bValue, base);
		}
		
	//*****************************************************************************
		
		public Y(double a, double b, double aValue, double bValue, LineDraw base, Object represents) {
			super(a, b, aValue, bValue, base, represents);
		}
		
	//*****************************************************************************
		
		public Vector<Object> getShapes() {
			Vector<Object> shapes = new Vector<Object>();
			
			ArrowDraw ad;
			double width = this.getLocation().distance(this.getEndLocation());
			
			double val = aValue;
			Point loc;
			double ang = base.getAngle();
			
			int div2 = 10;
			int div = div2;
			double rest = width%div;
			for (int i = 0; i < 9; i++) {
				div2++;
				if ((width%div2) < rest) {
					rest = width%div2;
					div = div2;
				}
			}
			
			int n = (int)(width/div);
			
			int sigA = 1;
			if (val >= 0) {
				sigA = 1;
			} else {
				sigA = -1;
			}
			int sigB;
			
			for (int i = 0; i <= n+1; i++) {
				
				val = ((bValue - aValue) * i * div)/width + aValue;
				if (val >= 0) {
					sigB = 1;
				} else {
					sigB = -1;
				}
				if ((aValue == 0) || (bValue == 0)) {
					sigB = sigA;
				}
				
				loc = new Point(this.getLocation().x + (int)((i * div)*Math.cos(ang)),
					this.getLocation().y + (int)((i * div)*Math.sin(ang)));
				
				ad = new ArrowDraw.Out();
				
				if (i < n) { 
					ad.setLocation(loc);
					/*if ( (i == 0) && (aValue != 0.0)) {
						ad.setLabel(aValue + "");
					}*/
					if (sigA == sigB) {
						ad.setAngle(this.getAngle());
						ad.setValue(val);
					} else {
						ad.setAngle(this.getAngle() - Math.PI);
						ad.setValue((-1)*val);
					}
				} else {
					ad.setLocation(this.getEndLocation());
					/*if (bValue != 0.0) {
						ad.setLabel(bValue + "");
					}*/
					if (sigA == sigB) {
						ad.setAngle(this.getAngle());
						ad.setValue(bValue);
					} else {
						ad.setAngle(this.getAngle() - Math.PI);
						ad.setValue((-1)*bValue);
					}
				}
				ad.setScale(this.getScale());
				
				shapes.add(ad);
			} 
			
			return shapes;
			
		}
		
	//*****************************************************************************
	}
	
//*****************************************************************************
//*****************************************************************************
}
